const { Code } = require('./code.js');
const { Parser } = require('./parser.js');
const { LittleIO } = require('./littleIO');
const { Symbol } = require('./symbol');

class Assembler {
  constructor(parser, code, io, symbol) {
    this.parser = parser;
    this.code = code;
    this.io = io;
    this.symbol = symbol;
  }

  firstRun() {
    this.io.loadFile();
    let lineCounter = 0;
    while (this.parser.hasMoreLines() && this.parser.advance()) {
      if (this.parser.isLInstruction()) {
        this.symbol.addEntry(this.parser.symbol(), lineCounter);
      } else {
        lineCounter++;
      }
    }
  }

  secondRun() {
    this.io.loadFile();
    let variableStart = 16;

    while (this.parser.hasMoreLines() && this.parser.advance()) {
      if (this.parser.isLInstruction()) continue;

      let machineCode;
      if (this.parser.isAInstruction()) {
        const sym = this.parser.symbol();
        if (/^\d/.test(sym)) {
          machineCode = this._coverTo16Bits(sym);
        } else if (this.symbol.contains(sym)) {
          machineCode = this._coverTo16Bits(this.symbol.getAddress(sym));
        } else {
          this.symbol.addEntry(sym, variableStart);
          machineCode = this._coverTo16Bits(variableStart);
          variableStart++;
        }
      } else {
        machineCode = [
          '111',
          this.code.comp(this.parser.comp()),
          this.code.dest(this.parser.dest()),
          this.code.jump(this.parser.jump())
        ].join('');
      }
      this.io.writeToCache(machineCode);
    }

    this.io.writeToFile();
  }

  // Decimal to Binary
  _coverTo16Bits(num) {
    return parseInt(num).toString(2).padStart(16, '0');
  }
}

const littleIO = new LittleIO();
const assembler = new Assembler(new Parser(littleIO), new Code(), littleIO, new Symbol());
assembler.firstRun();
assembler.secondRun();
